﻿using Log.Recorder.Entity;
using Newtonsoft.Json;
using System;
namespace Log.Recorder
{
    /// <summary>
    /// 内部日志记录器
    /// </summary>
    class InternalLogRecorder : IDisposable
    {
        /// <summary>
        /// 堆栈踪迹层数
        /// </summary>
        private int stackTraceLayer { get; set; }
        private static LogWriter writer
        {
            get { return LogWriter.StaticInstance; }
        }

        public InternalLogRecorder()
        {
            stackTraceLayer = 4;
        }
        /// <summary>
        /// 堆栈踪迹层数 （大于 3）
        /// </summary>
        /// <param name="stackTraceLayer"></param>
        public InternalLogRecorder(int stackTraceLayer)
        {
            this.stackTraceLayer = stackTraceLayer < 4 ? 4 : stackTraceLayer;
        }



        /// <summary>
        /// 生成堆栈踪迹信息
        /// </summary>
        /// <param name="memberName"></param>
        /// <returns></returns>
        private string CreateStackTrace([System.Runtime.CompilerServices.CallerMemberName] string memberName = null)
        {
            string _stackTrace = null;
            try
            {
                System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(true);
                System.Diagnostics.StackFrame frame = trace.GetFrame(stackTraceLayer);
                if (frame != null)
                {
                    System.Reflection.MethodBase method = frame.GetMethod();
                    if (method != null)
                    {
                        Type type = method.ReflectedType;
                        string projectName = System.Reflection.Assembly.GetAssembly(type).GetName().Name;
                        
                        //日志器内部路径
                        if (projectName == "Log.Recorder")
                        {
                            _stackTrace = string.Format("【记录位置】在 {0}.dll 的 {1}.{2} 方法中。"
                                    , projectName
                                    , type.FullName
                                    , method.Name
                                    );
                        }
                        else if(!string.IsNullOrEmpty(projectName)){
                            string filePath = frame.GetFileName();
                            if (!string.IsNullOrEmpty(filePath) && filePath.Contains(projectName))
                            {
                                filePath = filePath.Replace("\\", "/");
                                filePath = projectName + filePath.Split(new string[] { projectName }, StringSplitOptions.None)[1];
                                // 在 Namespace.className.methodName 方法中，文件位置 filePath ：行号 88 列号 8
                                _stackTrace = string.Format("【记录位置】在 {0}.dll 的 {1}.{2} 方法中，其文件位置为 {3} ：行号 {4} 列号 {5} 。"
                                    , projectName
                                    , type.FullName
                                    , method.Name
                                    //, System.Environment.NewLine
                                    , filePath
                                    , frame.GetFileLineNumber()
                                    , frame.GetFileColumnNumber()
                                );

                            }
                        }
                        
                        
                    }
                    
                }

                if (string.IsNullOrEmpty(_stackTrace))
                {
                    _stackTrace = Convert.ToString(trace);
                }

            }
            catch { }
            return _stackTrace;
        }


        private LogContent CreateLogContent(string levelStr, string message, object data, string logType, string fileName)
        {
            DateTime logTime = DateTime.Now;

            // 2021/06/20210603_debug.db
            string _fileName = string.Format("{0}/{1}/{2}_{3}.db"
                , logTime.ToString("yyyy")
                , logTime.ToString("MM")
                , logTime.ToString("yyyyMMdd")
                , levelStr
                );
            if (!string.IsNullOrEmpty(fileName))
            {
                _fileName = string.Format("{0}/{1}/{2}_{3}.db"
                , logTime.ToString("yyyy")
                , logTime.ToString("MM")
                , logTime.ToString("yyyyMMdd")
                , fileName.Trim()
                );
            }

            LogContent log = new LogContent(_fileName, logTime);
            log.Level = levelStr;
            log.Message = message;
            log.StackTrace = CreateStackTrace();

            #region data

            if (data == null || data is DBNull)
            {
                log.Data = "";
            }
            /*
            else if (Data is Exception) {
                Exception exp = (Exception) Data;
                string expData = LogJson.Serialize(exp.Data);
                _data = LogJson.Serialize(new Dictionary<string, string>() {
                    { "ExceptionMessage",exp.Message},
                    { "ExceptionData",expData},
                    { "ExceptionStackTrace",exp.StackTrace}
                });
            }
            */
            else if (
                data is sbyte || data is short || data is int || data is long || data is byte || data is ushort ||
                data is uint || data is ulong || data is decimal ||
                data is bool ||
                data is char || data is string || data is System.Text.StringBuilder
            )
            {
                log.Data = data.ToString();
            }
            else
            {
                JsonSerializerSettings settings = new JsonSerializerSettings();
                settings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                log.Data = JsonConvert.SerializeObject(data, settings);
            }

            #endregion

            log.Type = logType;

            return log;
        }

        public void DoRecord(LogLevel level, string message, object data, string logType, string fileName)
        {
            try
            {
                if (LogLevelHelper.IsCanRecord(level))
                {
                    string levelStr = level.ToString().ToLower();
                    if (string.IsNullOrEmpty(logType))
                    {
                        logType = "application";
                    }

                    LogContent content = CreateLogContent(levelStr, message, data, logType, fileName);

                    //写到日志池
                    writer.AddToLogPool(content);
                }

            }
            catch (Exception exp)
            {
                try
                {
                    InternalLog(level.ToString() + "记录器记录日志信息异常。" + exp.Message, exp);
                }
                catch { }
            }
        }
        /// <summary>
        /// 记录日志记录器内部异常用
        /// </summary>
        /// <param name="message"></param>
        /// <param name="data"></param>
        public void InternalLog(string message, object data = null)
        {
            DoRecord(LogLevel.RecorderLog, message, data, "LogRecorder", null);
        }


        #region 释放资源

        private bool disposedValue;
        public void Dispose()
        {
            if (!disposedValue)
            {

                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // TODO: 将大型字段设置为 null

                /*
                 托管对象指的是.net可以自动进行回收的资源，主要是指托管对象在堆上分配的内存资源。
                托管资源的回收工作是不需要人工干预的，有.net运行库在合适的时间进行回收。（手动回收GC.Collect）

                 非托管对象指.net不知道如何回收的资源，最常见的一类非托管资源是包装操作系统资源的对象，
                例如文件、窗口、网络连接、数据库连接、画刷、图标等。这类资源，垃圾回收器在清理的时候会调用Object.Finalize()方法。
                默认情况下，方法是空的，对于非托管对象在此方法中需要编写回收非托管对象的代码，以便垃圾回收器正确回收。(例如我们
                通常打开文件、图片等后需要进行Close()或者Dispose()去释放)。

                 */

                disposedValue = true;
            }
        }

        #endregion


    }
}
